package su2010.puz.util;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;

public class Cache implements Serializable {
	private static final long serialVersionUID = -3790298370803038728L;
	private Map<CacheKey, float[]> data = null;
	private boolean loaded = false;
	
	// Private constructor prevents instantiation from other classes
	private Cache() {
		data = new HashMap<Cache.CacheKey, float[]>();
	}
	
	public void add(String fileAbsPath, String extractorName, float[] values) {
		CacheKey ck = new CacheKey(fileAbsPath, extractorName);
		data.put(ck, values);
	}
	
	public float[] get(String fileAbsPath, String extractorName) {
		float[] values = data.get(new CacheKey(fileAbsPath, extractorName));
		if (values == null) return null;
		else return values;
	}
	
	public void save(String path) throws FileNotFoundException, IOException {
		ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(path));
		oos.writeObject(data);
		oos.close();
	}
	
	@SuppressWarnings({ "unchecked", "rawtypes" })
	public void load(String path) throws FileNotFoundException, IOException, ClassNotFoundException {
		if (loaded) return;
		File f = new File(path);
		if (!f.exists()) return;
		ObjectInputStream ois = new ObjectInputStream(new FileInputStream(f));
		Object o = ois.readObject();
		data = (Map) o;
		ois.close();
		loaded = true;
	}
	
	/**
	 * SingletonHolder is loaded on the first execution of
	 * Cache.instance() or the first access to SingletonHolder.INSTANCE,
	 * not before.
	 * 
	 * Pugh's singleton implementation (http://en.wikipedia.org/wiki/Singleton_pattern) 
	 */
	private static class SingletonHolder {
		public static final Cache INSTANCE = new Cache();
	}

	public static Cache instance() {
		return SingletonHolder.INSTANCE;
	}
	
	@Override
	public int hashCode() {
		return 1;
	}

	@Override
	public boolean equals(Object obj) {
		return true;
	}
	
	private class CacheKey implements Serializable {
		private static final long serialVersionUID = -7097472086964871816L;
		private String fileAbsPath;
		private String extractorName;
		
		public CacheKey(String fileAbsPath, String extractorName) {
			super();
			this.fileAbsPath = fileAbsPath;
			this.extractorName = extractorName;
		}

		@Override
		public int hashCode() {
			final int prime = 31;
			int result = 1;
			result = prime * result + getOuterType().hashCode();
			result = prime * result
					+ ((extractorName == null) ? 0 : extractorName.hashCode());
			result = prime * result
					+ ((fileAbsPath == null) ? 0 : fileAbsPath.hashCode());
			return result;
		}

		@Override
		public boolean equals(Object obj) {
			if (this == obj)
				return true;
			if (obj == null)
				return false;
			if (getClass() != obj.getClass())
				return false;
			CacheKey other = (CacheKey) obj;
			if (!getOuterType().equals(other.getOuterType()))
				return false;
			if (extractorName == null) {
				if (other.extractorName != null)
					return false;
			} else if (!extractorName.equals(other.extractorName))
				return false;
			if (fileAbsPath == null) {
				if (other.fileAbsPath != null)
					return false;
			} else if (!fileAbsPath.equals(other.fileAbsPath))
				return false;
			return true;
		}

		private Cache getOuterType() {
			return Cache.this;
		}

	}

}
